/*
 * With gdbus
 */
#include <stdio.h>
#include <stdlib.h>
#include <gio/gio.h>
#include "common.h"

#define OFONO_NOTIFICATION "org.ofono.PushNotification"
#define NOTIFICATION_AGENT "org.ofono.PushNotificationAgent"
#define NOTIFICATION_AGENT_PATH "/org/ofono/PushNotificationAgent"

static GDBusNodeInfo *introspection_data = NULL;

/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] = 
        "<node>"
        "   <interface name='org.ofono.PushNotificationAgent'>"
        "      <method name='Release'/>"
        "      <method name='ReceiveNotification'>"
        "         <arg type='ay' name='notification' direction='in'/>"
        "         <arg type='a{sv}' name='info' direction='in'/>"
        "      </method>"
        "   </interface>"
        "</node>";

static GMainLoop *main_loop;
static gchar *path = NULL;
static GVariant *agent_path = NULL;

static void agent_released() {
    g_print("Good bye cruel world\n");
    g_main_loop_quit(main_loop);
}

static void notification_received(GVariant *parameters) {
    g_print("parameters for notification_received is 0x%0x\n", (unsigned int) parameters);
    g_print("the type of parameters is '%s'\n", g_variant_get_type(parameters));
    g_print("the number of children %d\n", g_variant_n_children(parameters));
    GVariant *noti = g_variant_get_child_value(parameters, 0);
    gsize len = 0;
    const guchar *notification = g_variant_get_fixed_array(noti, &len, sizeof(guchar));
    hexdump(notification, len, "notification_received");

    GVariant *info = g_variant_get_child_value(parameters, 1);
    iterate_dictionary(info);
}

static void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path,
        const gchar *interface_name, const gchar *method_name, GVariant *parameters,
        GDBusMethodInvocation *invocation, gpointer user_data) {
    g_print("handle_method_call connection 0x%0x, sender %s, object path %s, interface name %s"
            ", method name %s\n", (unsigned int) connection, sender, object_path, interface_name, method_name);
    if (g_strcmp0(method_name, "Release") == 0) {
        g_print("org.ofono.PushNotificationAgent.Release gets called, vollia\n");
        agent_released();
    } else if (g_strcmp0(method_name, "ReceiveNotification") == 0) {
        g_print("org.ofono.PushNotificationAgent.ReceiveNotification gets called, vollia\n");
        notification_received(parameters);
    }
}

static const GDBusInterfaceVTable interface_vtable = {
    handle_method_call,
    NULL,
    NULL
};

static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) {
    g_print("on_bus_acquired connection is 0x%0x, name is '%s'\n", (unsigned int) connection, name);
    guint registration_id = g_dbus_connection_register_object(connection, path, introspection_data->interfaces[0],
            &interface_vtable, NULL /* user_data */, NULL /* user_data_free_func */, NULL /*GError */);
    g_assert(registration_id > 0);
    g_print("on_bus_acquired registration_id is %d\n", registration_id);
}

static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) {
    g_print("on_name_acquired connection 0x%x, name '%s', user data 0x%0x is acquired\n", (unsigned int) connection, name, (unsigned int) user_data);
}

static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data) {
    g_print("warning: on_name_lost connection 0x%x, name '%s', user data 0x%0x is lost\n", (unsigned int) connection, name, (unsigned int) user_data);
    g_print("warning: on_name_lost Bye--Bye--Bye--Bye\n");
}

int main(int argc, char **argv) {
    g_type_init();

    path = g_strdup(NOTIFICATION_AGENT_PATH);
    g_print("path '%s' is valid %d\n", path, g_variant_is_object_path(path));
    agent_path = g_variant_new("(o)", path);
    /* exporting PushNotificationAgent */
    introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
    g_assert(introspection_data != NULL);
    /* It seems that oFono connect to system dbus, so it cannot talks to session dbus,
     * need to find out where goes wrong. oFono should be able talks to session bus, since
     * phonesim is on session bus */
    const guint owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, NOTIFICATION_AGENT, G_BUS_NAME_OWNER_FLAGS_NONE, 
            on_bus_acquired, on_name_acquired, on_name_lost, 
            NULL /* user data */, NULL /* use data free func */);

    /* Let oFono know we exist */
    const GDBusConnection *conn = get_system_bus_connection();
    const gchar *modems[MODEM_COUNT_LIMIT];
    gsize modem_count = 0;
    get_modem_paths(modems, &modem_count);
    if (modem_count < 1) {
        g_print(" there are no modems available\n");
        return -1;
    }
    GError *error = NULL;
    GVariant *result = g_dbus_connection_call_sync(conn, OFONO_SERVICE_NAME, modems[0],
            OFONO_NOTIFICATION, "RegisterAgent", agent_path,
            NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
    if (result == NULL) {
        printf("error: %s\n", error->message);
        return -1;
    }
    
    g_print("starting loop to waiting for notification\n");
    main_loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(main_loop);

    /* cleanup objects */
    g_bus_unown_name(owner_id);
    g_dbus_node_info_unref(introspection_data);
    g_free(path);
    for (int i = 0; i < modem_count; i++) {
        g_free(modems[i]);
    }
    return 0;
}
